// Queue.java

package com.ibm.tspaces.examples.simple;

import com.ibm.tspaces.*;

/**
 * Class: Queue.java <br>
 *
 *		This class implements a simple efficient FIFO Queue.
 * <pre>
 *   Usage Example:
 *	 Thread1:
 *      Queue myqueue = new Queue(100);
 *      myqueue.put("This could be any Object");
 *   Thread2:
 *      String x = (String)myqueue.get();
 * </pre>
 *
 * Notes: <br>
 *   A previous version of this was implemented with 
 *   by extending a Vector.  But this should be more 
 *   efficient and has the builtin feature(limitation)
 *	 that it limits the length of the queue.  
 *   When the array is full,
 *   then queue.put(obj) will block until the next element
 *   has been gotten. 
 * @author		John Thomas<jthomas@cruzio.com>
 */


public class Queue   {

// Class Variables


// Instance Variables

/**
** Keep track of next element to give out
*/
private int _NextOut=0;

/**
** Keep track of the slot to store the next element in
*/
private int _NextIn = 0;

/**
** Slots to keep Queue elements
*/

private Object[] _Slots = null;

/**
** Provide debugging information
*/
private Thread	_WaitingForGet = null;
/**
** Provide debugging information
*/
private Thread  _WaitingForPut = null;
/*******************************************************************************
** Constructors **
******************/
/**
 * Queue Constructor. 
 *
 */

public 
Queue() {
    this(20);		// Default Max items in the queue
	
}

/**
 * Queue Constructor that sets the capacity of the Queue. 
 * When this many items are "put" to the queue, then it 
 * will block.  
 */
public 
Queue(int capacity) {
    _Slots = new Object[capacity];	
	
}









//--------------------------------------------------------------
/**
 * Method: isEmpty will return true if the Queue is empty..
 *
 *
 * @return boolean true if queue is empty.
 */
 //-------------------------------------------------------------
 

public synchronized boolean
isEmpty()  {
  
  return (_Slots[_NextOut] == null);  //return true  if queue is empty    
}




//--------------------------------------------------------------
/**
 * Method: get() will return the first object in the Queue.
 *
 * <p>
 * If the queue is empty, it will block until a new object arrives.
 *
 * @return Object  It will return the next Object in the Queue.
 */
 //-------------------------------------------------------------
 

public synchronized Object
get()	{
	Object obj=null;
	//Debug.out("Queue.get(): Size=" + toString());
	try {
		//Debug.assert(_WaitingForGet == null); // Temporary - remove it ASAP
		while (_Slots[_NextOut] == null) {		//This will block if queue is empty		
			_WaitingForGet = Thread.currentThread();			
			Debug.out("Queue.get() - Wait until item is in queue");
			wait();
		}
		_WaitingForGet = null;
		obj = _Slots[_NextOut];
		_Slots[_NextOut] = null;
		_NextOut++;
		if ( _NextOut == _Slots.length )
			_NextOut = 0;		
		notifyAll();
	} catch (Exception e) {
		// Possible exceptions are:
	
		//   InterruptedException
		//   IllegalMonitorException
		// This should never happen
		Debug.out("Queue.get() Failure",e);		
		System.exit(4);
	}
	
	return obj;
}







//-------------------------------------------------------------
/**
 * Method: put(Object obj) will place specified object into the FIFO Queue
 *
 * @param  obj		An Object that is to be added to the queue.
 * @return void
 *
 */
//-------------------------------------------------------------
 
public synchronized void
put(Object obj)		{
	
	try {
		if ( obj == null)  {  
			// We don't allow null to be placed in the queue
			// so we we treat it as a request to display the queue status 
			Debug.out(this.toString());
			if (_WaitingForPut != null) 
				Debug.out("Waiting for put(): "+ _WaitingForPut.getName());
			if (_WaitingForGet != null) 
				Debug.out("Waiting for get(): "+ _WaitingForGet.getName());
				
			return;      // ignore it
		}
	    while ( _Slots[_NextIn] != null ) {
	    	Debug.out("Queue Full!: " + toString());    	
	    	_WaitingForPut = Thread.currentThread();
	    	wait();           // Queue is full
	    }
	    _WaitingForPut = null;
	    _Slots[_NextIn] = obj;
	    _NextIn++;
		if ( _NextIn == _Slots.length )
			_NextIn = 0;		
			
		notifyAll();
	    //Debug.out("Queue.put(): Size=" + size());
		
		//The following will give the thread that is processing the
		// queue entries a bit more chance to handle them.
		// Maybe it should be an option.
		
		//Thread.currentThread().yield();
		
	} catch (Exception e) {
		// Possible exceptions are:	
		//   InterruptedException
		//   IllegalMonitorException
		// This should never happen
		Debug.out("Queue.put() Failure",e);		
		System.exit(4);
	}
	
}



/*******************************************************************************
** toString  **
******************/

/**
** Give Debug info
*/

public String 
toString() {
	StringBuffer sb = new StringBuffer(100);
	
	sb.append(this.getClass().getName() + ":");
	sb.append("  NextOut = " + _NextOut + "  NextIn = " + _NextIn + "\n");
	for (int i=0; i < _Slots.length; i++) {
		if (_Slots[i] != null) 
			sb.append("    " + i + "  " + _Slots[i] + "\n");
	}
	return sb.toString();
}
	
	


/*******************************************************************************
** main **
******************/
	
/**-
 * Method: main.
 * 		Adding this makes this class self-testing
 * @param	args	Command line arguments
 * @return 	void    Best if ended with System.exit(0)
 *   
 */
public static void
main(String args[]) {

/****** comment out this method ********************
int maxentries = 5;

	Debug.setDebugOn(true);
	if (args.length > 0) {	// 1st argument is debug output file name
		Debug.setDebugFile(args[0]);
	}
	Queue myqueue = new Queue(maxentries);

	QueueTester qt1 = new QueueTester("put",myqueue);
	QueueTester qt2 = new QueueTester("get",myqueue);
    try {
    	Thread t1 = new Thread(qt1);
    	Thread t2 = new Thread(qt2);
		t1.start();
		t2.start();
		t1.join();
		Debug.out("Thread t1 died");
		t2.join();
		Debug.out("Thread t2 died");
		
    } catch (Exception e) {
    	e.printStackTrace();
    }
    System.exit(0);
  /*  
  ************************************  comment out this method ***********/    
}
   

   
} //End of Queue class

/******* comment out this entire class definition ****
class QueueTester implements Runnable {
int numentries = 10;  // Number of entries to write.
int delay = 5;  // delay get this to test put overflow.

String type;
Queue  queue;

public
QueueTester(String mytype,Queue q) {
	type = mytype;
	queue = q;
	
}

public void
run() {
	Debug.out("Started " + type);
	if (type.equals("put") ) {   // this thread puts to queue
    	for (int i=0; i<numentries; i++) {
    		String s = "String " + i;
    		Debug.out("Put " + s);
    		queue.put(new String(s));
    		//try { Thread.currentThread().sleep(i*10); }
    		//catch (Exception e) {}
    	}
    	queue.put("quit");
    } else {
    	while(true) {
    		String s = (String)queue.get();
    		Debug.out("     Get " + s);
    		if ( s.equals("quit") )
    			break;
   			try { Thread.currentThread().sleep(delay); }
    		catch (Exception e) {}
	
    	}
    }
}

   
} //End of QueueTester class

/*
*****************************************************/
/* $Log: Queue.java,v $
/* Revision 2.1.2.1  2000/08/30 12:57:09  jthomas
/* update to use Queue class
/*
/* Revision 2.1  1999/11/05 22:22:40  estesp
/* Update revision number to 2.1 on all files
/*
/* Revision 1.1.1.1  1999/11/05 16:24:53  estesp
/* Imported 2.1.0 release into Austin CVS
/*
 * Revision 1.2  1999/06/17 05:39:50  thodes
 *   Updated the inital checkin to append the "Log" field and
 * convert CR/LF to CR (for win/unix interop).
 *
 *   The inital checkin lower-cased directrory names, and omitted
 * many dirs/files that will added later, e.g., MoDAL stuff.
 *
 * All of the above will need to be done to all VSS-to-CVS
 * converted sources.  In summary, in moving from VSS to CVS, do this:
 *  - lower-case dir names
 *  - CR/LF --> CR
 *  - append CVS "Log" entties
 *
 * (a script that helps with #2 and #3 is in
 *     tspaces1:~thodes/bin/update-keywords.sh)
 * 
 */


